/*
    Load Store Unit
*/	
`include "define.v"
module lsu
(
    input 	wire          	    clk,
    input 	wire          	    rstn,
    input 	wire          	    flush,
    /*   
        防止存储不该存储的数据
        如果前一条指令引发异常，则本条指令则不应该进行load/store   
    */
    input 	wire          	    lsu_exc_irq_flag_i,
    input 	wire          	    lsu_instr_load_i,
	input 	wire          	    lsu_instr_store_i,
	input   wire    [02:0]      lsu_funct3_i,
	input 	wire 	[63:0]	    lsu_mem_addr_i,
	input 	wire 	[63:0]      lsu_store_data_i,
	output 	wire         	    lsu_done_o,	
    output 	wire           	    lsu_flag_o,		
    output  wire    [63:0]      lsu_load_data_o,
    output  reg     [07:00]     lsu_byte_en_o,
    //
    input 	wire          	    WB_ACKi,
    input 	wire 	[63:0]      WB_DATi,
	output 	wire 	[63:0] 	    WB_ADRo,			
	output 	reg 	[63:0]      WB_DATo,
	output 	wire         	    WB_WEo,				
	output 	wire         	    WB_CYCo											
);
    //
/*  
    确保load/store信号只往总线发送一次
*/
    reg         lsu_ctrl_flag;      

    assign      lsu_flag_o      =   lsu_instr_load_i || lsu_instr_store_i;
    
    always @( posedge clk ) begin
        if( !rstn | flush )   begin
            lsu_ctrl_flag   <= 1'b0;
        end
        else    begin
            if          ( lsu_flag_o && !lsu_ctrl_flag && !lsu_done_o )                         lsu_ctrl_flag   <= 1'b1;
            else if     ( lsu_done_o  )                                                         lsu_ctrl_flag   <= 1'b0;
            else                                                                                lsu_ctrl_flag   <= 1'b0;
        end
    end
    
    assign lsu_done_o			=	WB_ACKi;



    /*
    Because the width of the data bus is 64, the address is divided into the following two parts.
    |0000|0000|0000|0000|0000|0000|0000 0|  0  0  0  |
    |--------------word_addr-------------|-byte_addr-|
*/
    //wire            [63:03]  word_addr;   // word address in perips
    wire            [02:00]  byte_addr;   // byte address in word

    //assign  word_addr           =   lsu_mem_addr_i[63:03];
    assign  byte_addr           =   lsu_mem_addr_i[02:00];    

    // WB_DATo is store data.
    // This name is because WishBone bus is used. You can also modify it to any bus you want.
    // The 'WB' is short for WishBone
    
    always @( * ) begin
        case (lsu_funct3_i)
            3'b000 :    begin       //  SB
                case (byte_addr) 
                    3'b000 :    begin
                                    lsu_byte_en_o   = 8'b0000_0001;
                                    WB_DATo         =   {56'b0,lsu_store_data_i[07:00]};
                                end 
                    3'b001 :    begin
                                    lsu_byte_en_o   = 8'b0000_0010;
                                    WB_DATo         =   {48'b0,lsu_store_data_i[07:00],8'b0};
                                end 
                    3'b010 :    begin
                                    lsu_byte_en_o   = 8'b0000_0100;
                                    WB_DATo         =   {40'b0,lsu_store_data_i[07:00],16'b0};
                                end 
                    3'b011 :    begin
                                    lsu_byte_en_o   = 8'b0000_1000;
                                    WB_DATo         =   {32'b0,lsu_store_data_i[07:00],24'b0};
                                end 
                    3'b100 :    begin
                                    lsu_byte_en_o   = 8'b0001_0000;
                                    WB_DATo         =   {24'b0,lsu_store_data_i[07:00],32'b0};
                                end 
                    3'b101 :    begin
                                    lsu_byte_en_o   = 8'b0010_0000;
                                    WB_DATo         =   {16'b0,lsu_store_data_i[07:00],40'b0};
                                end 
                    3'b110 :    begin
                                    lsu_byte_en_o   = 8'b0100_0000;
                                    WB_DATo         =   {8'b0,lsu_store_data_i[07:00],48'b0};
                                end 
                    3'b111 :    begin
                                    lsu_byte_en_o   = 8'b1000_0000;
                                    WB_DATo         =   {lsu_store_data_i[07:00],56'b0};
                                end 
                endcase
            end
            3'b001 :    begin       //  SH
                case (byte_addr) 
                    3'b000 :    begin
                                    lsu_byte_en_o   = 8'b0000_0011;
                                    WB_DATo         = {48'b0,lsu_store_data_i[15:00]}  ;
                                end //
                    3'b001 :    begin
                                    lsu_byte_en_o   = 8'b0000_0000;
                                    WB_DATo         = 64'b0  ;
                                end 
                    3'b010 :    begin
                                    lsu_byte_en_o   = 8'b0000_1100;
                                    WB_DATo         = {32'b0,lsu_store_data_i[15:00],16'b0}  ;
                                end //
                    3'b011 :    begin
                                    lsu_byte_en_o   = 8'b0000_0000;
                                    WB_DATo         = 64'b0  ;
                                end 
                    3'b100 :    begin
                                    lsu_byte_en_o   = 8'b0011_0000;
                                    WB_DATo         = {16'b0,lsu_store_data_i[15:00],32'b0}  ;
                                end //
                    3'b101 :    begin
                                    lsu_byte_en_o   = 8'b0000_0000;
                                    WB_DATo         = 64'b0  ;
                                end 
                    3'b110 :    begin
                                    lsu_byte_en_o   = 8'b1100_0000;
                                    WB_DATo         = {lsu_store_data_i[15:00],48'b0}  ;
                                end //
                    3'b111 :    begin
                                    lsu_byte_en_o   = 8'b0000_0000;
                                    WB_DATo         = 64'b0  ;
                                end 
                endcase
            end
            3'b010 :    begin       //  SW
                case (byte_addr) 
                    3'b000 :    begin        
                                    lsu_byte_en_o   = 8'b0000_1111;
                                    WB_DATo         =   {32'b0,lsu_store_data_i[31:00]};
                                end     //
                    3'b001 :    begin        
                                    lsu_byte_en_o   = 8'b0000_0000;
                                    WB_DATo         =   64'b0;
                                end 
                    3'b010 :    begin        
                                    lsu_byte_en_o   = 8'b0000_0000;
                                    WB_DATo         =   64'b0;
                                end 
                    3'b011 :    begin        
                                    lsu_byte_en_o   = 8'b0000_0000;
                                    WB_DATo         =   64'b0;
                                end 
                    3'b100 :    begin        
                                    lsu_byte_en_o   = 8'b1111_0000;
                                    WB_DATo         =   {lsu_store_data_i[31:00],32'b0};
                                end     //
                    3'b101 :    begin        
                                    lsu_byte_en_o   = 8'b0000_0000;
                                    WB_DATo         =   64'b0;
                                end 
                    3'b110 :    begin        
                                    lsu_byte_en_o   = 8'b0000_0000;
                                    WB_DATo         =   64'b0;
                                end 
                    3'b111 :    begin        
                                    lsu_byte_en_o   = 8'b0000_0000;
                                    WB_DATo         =   64'b0;
                                end 
                endcase
            end
            3'b011 :    begin       //  SD
                case (byte_addr) 
                    3'b000 :    begin
                                    lsu_byte_en_o = 8'b1111_1111;
                                    WB_DATo         =   lsu_store_data_i;
                                end 
                    3'b001 :    begin
                                    lsu_byte_en_o = 8'b0000_0000;
                                    WB_DATo         =   64'b0;
                                end 
                    3'b010 :    begin
                                    lsu_byte_en_o = 8'b0000_0000;
                                    WB_DATo         =   64'b0;
                                end 
                    3'b011 :    begin
                                    lsu_byte_en_o = 8'b0000_0000;
                                    WB_DATo         =   64'b0;
                                end 
                    3'b100 :    begin
                                    lsu_byte_en_o = 8'b0000_0000;
                                    WB_DATo         =   64'b0;
                                end 
                    3'b101 :    begin
                                    lsu_byte_en_o = 8'b0000_0000;
                                    WB_DATo         =   64'b0;
                                end 
                    3'b110 :    begin
                                    lsu_byte_en_o = 8'b0000_0000;
                                    WB_DATo         =   64'b0;
                                end 
                    3'b111 :    begin
                                    lsu_byte_en_o = 8'b0000_0000;
                                    WB_DATo         =   64'b0;
                                end 
                endcase
            end
            default :   begin
                lsu_byte_en_o   =   8'b0;
                WB_DATo         =   64'b0;  
            end
        endcase
    end


    // Wishbone control signal,you can also modify it to any bus you want.
    assign WB_ADRo			    =	(lsu_instr_load_i | lsu_instr_store_i) ? lsu_mem_addr_i : 64'b0;
    //  
	assign WB_WEo   		    =	lsu_instr_store_i  && !lsu_ctrl_flag && !lsu_exc_irq_flag_i;
	assign WB_CYCo			    =	(lsu_instr_load_i | lsu_instr_store_i) && !lsu_ctrl_flag && !lsu_exc_irq_flag_i;

	 // lsu_load_data_o is load data
	assign lsu_load_data_o	        =	(lsu_funct3_i == 3'b000 && byte_addr == 3'b000 ? {{56{WB_DATi[07]}}, WB_DATi[07:00]} : 64'b0) |     // LB
                                        (lsu_funct3_i == 3'b000 && byte_addr == 3'b001 ? {{56{WB_DATi[15]}}, WB_DATi[15:08]} : 64'b0) |
                                        (lsu_funct3_i == 3'b000 && byte_addr == 3'b010 ? {{56{WB_DATi[23]}}, WB_DATi[23:16]} : 64'b0) |
                                        (lsu_funct3_i == 3'b000 && byte_addr == 3'b011 ? {{56{WB_DATi[31]}}, WB_DATi[31:24]} : 64'b0) |
                                        (lsu_funct3_i == 3'b000 && byte_addr == 3'b100 ? {{56{WB_DATi[39]}}, WB_DATi[39:32]} : 64'b0) |
                                        (lsu_funct3_i == 3'b000 && byte_addr == 3'b101 ? {{56{WB_DATi[47]}}, WB_DATi[47:40]} : 64'b0) |
                                        (lsu_funct3_i == 3'b000 && byte_addr == 3'b110 ? {{56{WB_DATi[55]}}, WB_DATi[55:48]} : 64'b0) |
                                        (lsu_funct3_i == 3'b000 && byte_addr == 3'b111 ? {{56{WB_DATi[63]}}, WB_DATi[63:56]} : 64'b0) |

							            (lsu_funct3_i == 3'b001 && byte_addr == 3'b000 ? {{48{WB_DATi[15]}}, WB_DATi[15:00]} : 64'b0) |     // LH
							            (lsu_funct3_i == 3'b001 && byte_addr == 3'b010 ? {{48{WB_DATi[31]}}, WB_DATi[31:16]} : 64'b0) |
                                        (lsu_funct3_i == 3'b001 && byte_addr == 3'b100 ? {{48{WB_DATi[47]}}, WB_DATi[47:32]} : 64'b0) |
                                        (lsu_funct3_i == 3'b001 && byte_addr == 3'b110 ? {{48{WB_DATi[63]}}, WB_DATi[63:48]} : 64'b0) |
                                                                                                                                
                                        (lsu_funct3_i == 3'b010 && byte_addr == 3'b000 ? {{32{WB_DATi[31]}}, WB_DATi[31:00]} : 64'b0) |    // LW
                                        (lsu_funct3_i == 3'b010 && byte_addr == 3'b100 ? {{32{WB_DATi[63]}}, WB_DATi[63:32]} : 64'b0) |

                                        (lsu_funct3_i == 3'b011 && byte_addr == 3'b000 ? { WB_DATi                         } : 64'b0) |    // LD

                                
                                        (lsu_funct3_i == 3'b100 && byte_addr == 3'b000 ? {56'b0            , WB_DATi[07:00]} : 64'b0) |    // LBU
                                        (lsu_funct3_i == 3'b100 && byte_addr == 3'b001 ? {56'b0            , WB_DATi[15:08]} : 64'b0) |
                                        (lsu_funct3_i == 3'b100 && byte_addr == 3'b010 ? {56'b0            , WB_DATi[23:16]} : 64'b0) |
                                        (lsu_funct3_i == 3'b100 && byte_addr == 3'b011 ? {56'b0            , WB_DATi[31:24]} : 64'b0) |
                                        (lsu_funct3_i == 3'b100 && byte_addr == 3'b100 ? {56'b0            , WB_DATi[39:32]} : 64'b0) |
                                        (lsu_funct3_i == 3'b100 && byte_addr == 3'b101 ? {56'b0            , WB_DATi[47:40]} : 64'b0) |
                                        (lsu_funct3_i == 3'b100 && byte_addr == 3'b110 ? {56'b0            , WB_DATi[55:48]} : 64'b0) |
                                        (lsu_funct3_i == 3'b100 && byte_addr == 3'b111 ? {56'b0            , WB_DATi[63:56]} : 64'b0) |

							            (lsu_funct3_i == 3'b101 && byte_addr == 3'b000 ? {48'b0            , WB_DATi[15:00]} : 64'b0) |     // LHU
							            (lsu_funct3_i == 3'b101 && byte_addr == 3'b010 ? {48'b0            , WB_DATi[31:16]} : 64'b0) |
                                        (lsu_funct3_i == 3'b101 && byte_addr == 3'b100 ? {48'b0            , WB_DATi[47:32]} : 64'b0) |
                                        (lsu_funct3_i == 3'b101 && byte_addr == 3'b110 ? {48'b0            , WB_DATi[63:48]} : 64'b0) |
                                                                                                                                
                                        (lsu_funct3_i == 3'b110 && byte_addr == 3'b000 ? {32'b0            , WB_DATi[31:00]} : 64'b0) |     // LWU
                                        (lsu_funct3_i == 3'b110 && byte_addr == 3'b100 ? {32'b0            , WB_DATi[63:32]} : 64'b0) ;
                                                                                                                                

endmodule